#include "ZAFileExplorer.h"

#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "advapip.lib")

WCHAR g_ZADevName[MAX_PATH*2] = L"";
WCHAR g_ZANtFolderName[MAX_PATH*2] = L"";

BOOLEAN ZAListDir(IN CHAR *pszFilePath)
{
	WIN32_FIND_DATA		FindFileData = {0};
	HANDLE				hFindFile;
	CHAR				*pFileDir;
	CHAR				*pFilePath;
	CHAR				*pTempFilePath;
	CHAR				*pTempFileDir;
	CHAR				*szTempFilePath;
	DWORD				dwFileLength;
	BOOLEAN				dwResult;

	dwResult = FALSE;

	pFilePath = (CHAR*)VirtualAlloc(NULL, MAX_PATH + 1, MEM_COMMIT, PAGE_READWRITE);
	pFileDir  = (CHAR*)VirtualAlloc(NULL, MAX_PATH + 1, MEM_COMMIT, PAGE_READWRITE);

	pTempFilePath = pFilePath;
	pTempFileDir = pFileDir;
	szTempFilePath = pszFilePath;

	if (!pFilePath || !pFileDir) return dwResult;

	ZeroMemory(pFilePath, MAX_PATH + 1);
	ZeroMemory(pFileDir, MAX_PATH + 1);

	StringCchLength(pszFilePath, MAX_PATH + 1, &dwFileLength);

	if (*(CHAR*)(pszFilePath + dwFileLength) != '\\') {
		*(CHAR*)(pszFilePath + dwFileLength) = '\\';
		*(CHAR*)(pszFilePath + dwFileLength+1) = '\0';
	}

	StringCchCopy(pFilePath, MAX_PATH + 1, pszFilePath);

	StringCchCopy(pFileDir, MAX_PATH + 1, pszFilePath);

	StringCchCat(pFilePath, MAX_PATH + 1, "*");

	hFindFile = FindFirstFile(pFilePath, &FindFileData);

	if (hFindFile != INVALID_HANDLE_VALUE)
	{
		do{
			pFilePath = strrchr(pFilePath, '\\');

			pFilePath = pFilePath + 1;

			StringCchLength(pFilePath, 0x100, &dwFileLength);

			ZeroMemory(pFilePath, dwFileLength);

			pFilePath = pTempFilePath;

			StringCchCat(pFilePath, MAX_PATH + 1, FindFileData.cFileName);

			if (_stricmp(FindFileData.cFileName, ".") == 0 ||
				_stricmp(FindFileData.cFileName, "..") == 0 ) continue;

			printf("%s\n", pFilePath);

			// Directory
			if (FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
			{
				ZAListDir(pFilePath);
			}


		}while(FindNextFile(hFindFile, &FindFileData));
	}

	if (hFindFile != INVALID_HANDLE_VALUE)
	{
		StringCchLength(pszFilePath, MAX_PATH + 1, &dwFileLength); // dwFileLength = Including null terminating character

		dwFileLength -= 1;

		// Go back up to one directory (..) of the current folder and return
		if (*(CHAR*)(pszFilePath + dwFileLength) == '\\') 
			*(CHAR*)(pszFilePath + dwFileLength) = '\0';

		pszFilePath = strrchr(pszFilePath, '\\');

		pszFilePath = pszFilePath + 1;

		StringCchLength(pszFilePath, 0x100, &dwFileLength);

		ZeroMemory(pszFilePath, dwFileLength);

		pszFilePath = szTempFilePath;

		FindClose(hFindFile);
	}

	dwResult = TRUE;

	VirtualFree(pFilePath, 0, MEM_RELEASE);
	VirtualFree(pFileDir, 0, MEM_RELEASE);

	return dwResult;
}

DWORD IncreaseProcPriviledge(CHAR *szPriviledgeName)
{
	BOOL	result;
	HANDLE	hToken;
	TOKEN_PRIVILEGES token;

	token.PrivilegeCount = 1;

	if (OpenProcessToken(GetCurrentProcess(), 
		TOKEN_ADJUST_PRIVILEGES,
		&hToken) && 
		LookupPrivilegeValue(NULL,
		szPriviledgeName,
		&token.Privileges[0].Luid))
	{
		token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
		AdjustTokenPrivileges(hToken,
			FALSE,
			&token,
			sizeof(TOKEN_PRIVILEGES),
			NULL,
			NULL);
	}

	result = GetLastError();

	CloseHandle(hToken);

	return result;
}

BOOL CheckRootkitDevice()
{
	DWORD				dwResult;
	int					i = 0;
	HANDLE				hFile = 0;
	IO_STATUS_BLOCK		is;
	OBJECT_ATTRIBUTES	oa;
	UNICODE_STRING		us;

	dwResult = FALSE;

	RtlInitUnicodeString(&us, g_ZADevName);

	InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE, NULL, NULL);

	dwResult = ZwOpenFile(&hFile, FILE_SEQUENTIAL_WRITE_ONCE, &oa, &is, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_SUPPORTS_OBJECT_IDS);
		
	ZwClose(hFile);

	if (dwResult == STATUS_VALIDATE_CONTINUE)
		dwResult = TRUE;
	else
		dwResult = FALSE;

	
	return dwResult;
}

int main(void)
{
	FILE_FS_VOLUME_INFORMATION	volinfo;
	IO_STATUS_BLOCK isb;
	HANDLE	hTargetPath;
	DWORD	ntstatus;
	
	hTargetPath = CreateFile(TARGET_PATH, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, 0);

	// Unable to open the directory
	if (hTargetPath == INVALID_HANDLE_VALUE)
	{
		printf("[-] Unable to open the directory. (0x%08x)\n", GetLastError());
		goto FINISH;
	}
	else
		{
			// Get the volume creation time here
			if (DEBUG) printf("[DEBUG] Handle file to C:\\Windows: 0x%x\n", hTargetPath);

			// Get volume information
			ntstatus = ZwQueryVolumeInformationFile(hTargetPath, &isb, &volinfo, 0x18, FileFsVolumeInformation);

			// We do not need the directory handle anymore
			CloseHandle(hTargetPath);

			// Get volume information succeed
			if (NT_SUCCESS(ntstatus))
			{
				MD5_CTX context; 
				DWORD md5digest;
				DWORD dwFolderNumber;
				BOOL bZADevName;
				WORD a, b;

				// Print volume creation time debug message
				if (DEBUG)
				{
					printf("[DEBUG] Volume creation time low part: 0x%08x\n", volinfo.VolumeCreationTime.LowPart);
					printf("[DEBUG] Volume creation time high part: 0x%08x\n", volinfo.VolumeCreationTime.HighPart);
				}

				// CLSID generation algorithm
				MD5Init(&context);   
				MD5Update(&context, (unsigned char*)&volinfo.VolumeCreationTime, 8);   
				MD5Final(&context);   

				md5digest = *(DWORD*)((DWORD)&context.digest);
				md5digest ^=  *(DWORD*)((DWORD)&context.digest + 4);
				md5digest ^=  *(DWORD*)((DWORD)&context.digest + 8);
				md5digest ^=  *(DWORD*)((DWORD)&context.digest + 12);

				a =  md5digest >> 16;
				b =  md5digest;

				dwFolderNumber = a ^ b;

				printf("[+] MD5 digest: 0x%08x\n", md5digest);

				// Format ZeroAccess device name
				swprintf(g_ZADevName, 24, L"\\??\\%08x", md5digest);

				// Format ZeroAccess $NtUninstallKB folder name
				swprintf(g_ZANtFolderName, 100, L"\\systemroot\\$NtUninstallKB%u$", (WCHAR*)dwFolderNumber);
				
				wprintf(L"[+] ZA rootkit device name: %ls\n", g_ZADevName);
				wprintf(L"[+] ZA NT folder name: %ls\n", g_ZANtFolderName);

				// Check ZeroAccess rootkit device name existence
				bZADevName = CheckRootkitDevice();

				// ZeroAccess rootkit device name exists
				if (bZADevName)
				{
					//SID_IDENTIFIER_AUTHORITY sida = SECURITY_NT_AUTHORITY;
					SID_IDENTIFIER_AUTHORITY sida = SECURITY_LOCAL_SID_AUTHORITY;
					SECURITY_DESCRIPTOR sdOwner;
					PSID sidOwner;
					HANDLE hZADev; 
					OBJECT_ATTRIBUTES oaZADev = {0};
					UNICODE_STRING usZADev = {0};
					IO_STATUS_BLOCK isb = {0};

					printf("[+] Found existence of ZeroAccess rootkit device, continue...\n");

					if (DEBUG) __debugbreak();

					IncreaseProcPriviledge("SeTcbPrivilege");

					// For WRITE_OWNER
					IncreaseProcPriviledge("SeTakeOwnershipPrivilege");

					// For WRITE_DAC
					IncreaseProcPriviledge("SeRestorePrivilege");

					// Initialize object attributes for ZeroAccess device name
					RtlInitUnicodeString(&usZADev, g_ZANtFolderName);
					InitializeObjectAttributes(&oaZADev, &usZADev, OBJ_CASE_INSENSITIVE, NULL, NULL );

					// Open the symbolic link directory as read only volume
					ntstatus = ZwOpenFile(&hZADev, WRITE_OWNER, &oaZADev, &isb, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_REPARSE_POINT);
					//ntstatus = ZwOpenFile(&hZADev, FILE_LIST_DIRECTORY, &oaZADev, &isb, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_REPARSE_POINT|FILE_DIRECTORY_FILE);
			
					// If WRITE_OWNER handle success
					if (NT_SUCCESS(ntstatus))
					{

						printf("[+] Handle to zeroaccess directory [WRITE_OWNER]: 0x%x\n", hZADev);

						//ntstatus = RtlCreateAcl(&dacl, 0x100, ACL_REVISION);

						//ntstatus = RtlAllocateAndInitializeSid(&sida, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &sidOwner); // We need SYSTEM account privilege in order to open handle for WRITE_DAC otherwise, it will always return STATUS_ACCESS_DENIED. Refer to ZA's ZwAdjustPrviligesToken
						ntstatus = RtlAllocateAndInitializeSid(&sida, 1, SECURITY_LOCAL_RID, 0, 0, 0, 0, 0, 0, 0, &sidOwner);
						
						// Allow everyone access
						//ntstatus = RtlAddAccessAllowedAce(&dacl, ACL_REVISION, FILE_ALL_ACCESS, sidOwner);


						// Create Owner security descriptor
						ntstatus = RtlCreateSecurityDescriptor(&sdOwner, SECURITY_DESCRIPTOR_REVISION);

						// Set Owner field
						ntstatus = RtlSetOwnerSecurityDescriptor(&sdOwner, sidOwner, FALSE);

						// Set DACL to security descriptor
						//ntstatus = RtlSetDaclSecurityDescriptor(&sdOwner, TRUE, &dacl, FALSE);

						// Set security descriptor control to SE_DACL_PROTECTED|SE_DACL_PRESENT
						SetSecurityDescriptorControl(&sdOwner, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
						SetSecurityDescriptorDacl(&sdOwner, TRUE, NULL, FALSE);

						// Modified owner security information
						ntstatus = NtSetSecurityObject(hZADev, OWNER_SECURITY_INFORMATION, &sdOwner);

						printf("[+] Reseting Owner...");

						if (NT_SUCCESS(ntstatus))
							printf(" OK!\n");
						else
							printf(" Failed!\n");

						ZwClose(hZADev);

					}
					
					// If WRITE_DAC handle success
					if (NT_SUCCESS(ZwOpenFile(&hZADev, WRITE_DAC, &oaZADev, &isb, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_REPARSE_POINT)))
					{
						printf("[+] Handle to zeroaccess directory [WRITE_DAC]: 0x%x\n", hZADev);

						//ntstatus = RtlCreateAcl(&dacl, 0x100, ACL_REVISION);

						//ntstatus = RtlAllocateAndInitializeSid(&sida, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &sidOwner);
						ntstatus = RtlAllocateAndInitializeSid(&sida, 1, SECURITY_LOCAL_RID, 0, 0, 0, 0, 0, 0, 0, &sidOwner);

						// Allow everyone access
						//ntstatus = RtlAddAccessAllowedAce(&dacl, ACL_REVISION, FILE_ALL_ACCESS, sidOwner);


						// Create Owner security descriptor
						ntstatus = RtlCreateSecurityDescriptor(&sdOwner, SECURITY_DESCRIPTOR_REVISION);

						// Set Owner field
						ntstatus = RtlSetOwnerSecurityDescriptor(&sdOwner, sidOwner, FALSE);

						// Set DACL to security descriptor
						//ntstatus = RtlSetDaclSecurityDescriptor(&sdOwner, TRUE, &dacl, FALSE);

						// Set security descriptor control to SE_DACL_PROTECTED|SE_DACL_PROTECTED
						SetSecurityDescriptorControl(&sdOwner, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
						SetSecurityDescriptorDacl(&sdOwner, TRUE, NULL, FALSE);

						// Modified discretionary access control list of the owner field
						ntstatus = NtSetSecurityObject(hZADev, DACL_SECURITY_INFORMATION, &sdOwner);

						printf("[+] Reseting DACL...");

						if (NT_SUCCESS(ntstatus))
							printf(" OK!\n");
						else
							printf(" Failed!\n");

						ZwClose(hZADev);

					}

					if (DEBUG) __debugbreak();

					// Delete symbolic link attribute
					if (NT_SUCCESS(ZwOpenFile(&hZADev, 0x13019F, &oaZADev, &isb, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_REPARSE_POINT|FILE_SYNCHRONOUS_IO_NONALERT)))
					{
						REPARSE_GUID_DATA_BUFFER InputBuffer = {0};
						CHAR szZANtFolderName[MAX_PATH] = {0};
						
						InputBuffer.ReparseTag = IO_REPARSE_TAG_SYMLINK;

						ntstatus = ZwFsControlFile(hZADev, 0, 0, 0, &isb, FSCTL_DELETE_REPARSE_POINT, &InputBuffer, 0x18, 0, 0);

						ZwClose(hZADev);

						// List files in ZeroAccess directory

						sprintf(szZANtFolderName, "C:\\WINDOWS\\$NtUninstallKB%u$", (CHAR*)dwFolderNumber);

						printf("[+] Listing directory: %s\n\n", szZANtFolderName);
						printf("*******************************\n");
						ZAListDir(szZANtFolderName);
						printf("*******************************\n");

					}
				
				}	
				else
				{
					printf("[-] No ZeroAccess rootkit device found, quit.\n");
					goto FINISH;
				}

			}
			else
			{
				// Get error message why failed to get the volume information
				printf("[-] Failed to get volume information. (0x%08x)\n", ntstatus);
			}
		}

FINISH:
	printf("\nDone!\n");
	system("pause");
	return 0;
}